using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Puerts;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

namespace JetBrains.Rider.Unity.Editor
{
    public class TypeScriptOpenAsset
    {
        private static string ourEntryPointTypeName = "JetBrains.Rider.Unity.Editor.PluginEntryPoint";

        private static Assembly ourEditorPluginAssembly;

        public static Assembly EditorPluginAssembly
        {
            get
            {
                if (ourEditorPluginAssembly != null)
                    return ourEditorPluginAssembly;
                var assemblies = AppDomain.CurrentDomain.GetAssemblies();
                ourEditorPluginAssembly = assemblies.FirstOrDefault(a => a.GetName().Name.Equals("JetBrains.Rider.Unity.Editor.Plugin.Full.Repacked"));
                return ourEditorPluginAssembly;
            }
        }
        
        [OnOpenAsset(0)]
        public static bool OpenTextAsset(int instanceID, int line)
        {
            string path = AssetDatabase.GetAssetPath(instanceID);
            if (!path.EndsWith(".ts") && !path.EndsWith(".js"))
                return OpenLoggerStackTrace(instanceID,line);
            if (path.EndsWith(".ts") || path.EndsWith(".js"))
                return OpenFileDllImplementation(path, 0, 0);
            return false;
        }

        public static bool OpenFileDllImplementation(string path,int line,int col)
        {
            #if UNITY_2020_3_OR_NEWER
            return  Packages.Rider.Editor.EditorPluginInterop.OpenFileDllImplementation(path, line, col);
            #endif
            
            var openResult = false;
            // reflection for fast OpenFileLineCol, when Rider is started and protocol connection is established
            try
            {
                var assembly = EditorPluginAssembly;
                if (assembly == null) return false;
                var type = assembly.GetType(ourEntryPointTypeName);
                if (type == null) return false;
                var field = type.GetField("OpenAssetHandler", BindingFlags.NonPublic | BindingFlags.Static);
                if (field == null) return false;
                var handlerInstance = field.GetValue(null);
                var method = handlerInstance.GetType()
                    .GetMethod("OnOpenedAsset", new[] {typeof(string), typeof(int), typeof(int)});
                if (method == null) return false;
                var assetFilePath = path;
                if (!string.IsNullOrEmpty(path))
                    assetFilePath = Path.GetFullPath(path);
        
                openResult = (bool) method.Invoke(handlerInstance, new object[] {assetFilePath, line,col});
            }
            catch (Exception e)
            {
                Debug.Log("Unable to do OpenFile to Rider from dll, fallback to com.unity.ide.rider implementation.");
                Debug.LogException(e);
            }

            return openResult;
        }

        //log print open js
        //匹配文件后缀名
        static readonly string[] suffix = new[] { ".js", ".ts" };
        //匹配文件名和行号
        static readonly Regex regexLine = new Regex(string.Format(@"[ ]*at (.+)({0})\:(\d+)\:(\d+)\)?\r?\n", string.Join("|", suffix)));
        static readonly Regex regexEx = new Regex(string.Format(@"[ ]*at (.+) \((.+)({0})\:(\d+)\:(\d+)\)", string.Join("|", suffix)));

        static bool OpenLoggerStackTrace(int instanceID, int lineId)
        {
            var track = GetStackTrace();
            if (string.IsNullOrEmpty(track))
                return false;
            //匹配文件(路径/后缀名/行数/列数)
            var match = regexLine.Match(track);
            while (match.Success)
            {
                var filepath = match.Groups[1].Value + match.Groups[2].Value;
                var line = int.Parse(match.Groups[3].Value);
                var column = int.Parse(match.Groups[4].Value);
                //匹配带括号的输出
                var matchEx = regexEx.Match(match.Groups[0].Value);
                if (matchEx.Success)
                {
                    try
                    {
                        var _filepath = matchEx.Groups[2].Value + matchEx.Groups[3].Value;
                        var _line = int.Parse(matchEx.Groups[4].Value);
                        var _column = int.Parse(matchEx.Groups[5].Value);
                        filepath = _filepath;
                        line = _line;
                        column = _column;
                    }
                    catch { }
                }

                string ts = GetTsFilePath(filepath);
                if (File.Exists(ts) && EditorTSConfig.Instance.LoggerOpenTs)
                    return OpenFileDllImplementation(ts, line, column);

                if (File.Exists(filepath))
                    return OpenFileDllImplementation(filepath, line, column);

                match = match.NextMatch();
            }
            return false;
        }
        static string GetStackTrace()
        {
            var editorAssembly = Assembly.GetAssembly(typeof(EditorWindow));
            var consoleWindowType = editorAssembly.GetType("UnityEditor.ConsoleWindow", false);
            var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow", BindingFlags.Static | BindingFlags.NonPublic);

            var consoleWindow = fieldInfo.GetValue(null) as EditorWindow;
            if (consoleWindow != EditorWindow.focusedWindow)
            {
                return null;
            }
            var activeTextFieldInfo = consoleWindowType.GetField("m_ActiveText", BindingFlags.Instance | BindingFlags.NonPublic);

            return (string)activeTextFieldInfo.GetValue(consoleWindow);
        }

        static string GetTsFilePath(string js)
        {
            string jsFile = js.Replace("\\", "/");
            string jsPath = JsLoader.PathJs.Replace("\\", "/");
            string tspath = JsLoader.PathTs.Replace("\\", "/");
            string ts = jsFile.Replace(jsPath,tspath).Replace(".js", ".ts");
            return ts;
        }
    }
}